// 
//  Copyright (C) 2009 Jacek Trubłajewicz
// 
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//  
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// 

using System;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using Mono.Unix;
using HAKGERSoft;
using NLog;

namespace wxGadu
{

	/// <summary>
	/// Manages user descriptions
	/// </summary>
	public class DescriptionManager
	{
		private const int SIZE = 10;
		private string[] descriptions;
		private MainFrame mainFrame;
		private sHGG gadu;
		private wx.ComboBox descriptionCombo;
		private Mutex descriptionsMutex;
		private FileStream fs;
#if LOG
		private Logger logger = LogManager.GetCurrentClassLogger();
#endif
		
		/// <summary>
		/// Ctor
		/// </summary>
		/// <param name="parent">
		/// A <see cref="MainFrame"/>
		/// main application window
		/// </param>
		/// <param name="gadu">
		/// A <see cref="sHGG"/>
		/// object of network lib
		/// </param>
		public DescriptionManager(MainFrame parent, sHGG gadu)
		{
			mainFrame = parent;
			this.gadu = gadu;
			descriptionCombo = mainFrame.BottomWindow.UserDescriptionCombo;
			descriptionsMutex = new Mutex();
			
			LoadDescriptions();
		}
		
		/// <summary>
		/// Stores description list into a file
		/// </summary>
		/// <remarks>
		/// Uses xml serialization
		/// </remarks>
		public void SaveDescriptions()
		{
#if LOG
			logger.Trace(String.Format(Catalog.GetString("Saving descriptions list into: {0}"), mainFrame.AppCfg.DescriptionsFile));
#endif
			descriptionsMutex.WaitOne();
			
			try
			{
				fs = new FileStream(mainFrame.AppCfg.DescriptionsFile, FileMode.Create, FileAccess.Write);
				XmlSerializer ser = new XmlSerializer(typeof(string[]));
				ser.Serialize(fs, descriptions);
			}
			catch (Exception e)
			{
#if LOG
				logger.Error(String.Format(Catalog.GetString("Error while saving descriptions file: {0}. {1}"), mainFrame.AppCfg.DescriptionsFile, e.Message));
#endif
			}
			finally
			{
				if (fs != null)
				{
					fs.Close();
					fs = null;
				}
				descriptionsMutex.ReleaseMutex();
			}
		}
		
		/// <summary>
		/// Loads description list from file
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/>
		/// array containig loaded descriptions
		/// </returns>
		public string[] LoadDescriptions()
		{
			descriptionsMutex.WaitOne();
#if LOG
			logger.Trace(Catalog.GetString("Loading descriptions list"));
#endif
			
			try
			{
				fs = new FileStream(mainFrame.AppCfg.DescriptionsFile, FileMode.Open, FileAccess.Read);
				XmlSerializer ser = new XmlSerializer(typeof(string[]));
				descriptions = (string[]) ser.Deserialize(fs);
				// check sizes
				if (descriptions.Length > SIZE)
				{
					string[] tmpDesc = new string[SIZE];
					for (int i = 0; i < SIZE; i++) 
					{
						if (descriptions[i] == null)
							break;
						tmpDesc[i] = descriptions[i];
					}
					descriptions = tmpDesc;
				}
				
				if (descriptions.Length < SIZE)
				{
					string[] tmpDesc = new string[SIZE];
					for (int i = 0; i < descriptions.Length; i++) 
						tmpDesc[i] = descriptions[i];
					descriptions = tmpDesc;
				}					
			}
			catch (Exception e)
			{
#if LOG
				logger.Error(String.Format(Catalog.GetString("Error while reading descriptions file: {0}. {1}"), mainFrame.AppCfg.DescriptionsFile, e.Message));
#endif
				descriptions = new string[SIZE];
			}
			finally
			{
				if (fs != null)
				{
					fs.Close();
					fs = null;
				}
				descriptionsMutex.ReleaseMutex();
			}

			return descriptions;
		}
		
		/// <summary>
		/// Adds new description to list,
		/// updates gui and sets description in
		/// network object
		/// </summary>
		/// <param name="newDesc">
		/// A <see cref="System.String"/>
		/// description to be added
		/// </param>
		public void AddDescription(string newDesc)
		{
#if LOG
			logger.Trace(Catalog.GetString("Adding new description to descriptions list"));
#endif
			
			if (newDesc.Trim() == string.Empty)
			{
				descriptionCombo.Value = string.Empty;
				mainFrame.Dispatch.Gadu.GGDescription = string.Empty;
				// description should stay empty after app start
				mainFrame.AppCfg.UserConf.EmptyDesc = true;
				return;
			}
				
			descriptionsMutex.WaitOne();
			
			// move descriptions in list by one and insert new desc to the top
			for (int i = SIZE-1; i > 0; i--)
				descriptions[i] = descriptions[i-1];
			descriptions[0] = newDesc;
			
			// update description combo control
			if (descriptionCombo.Count == SIZE)
				descriptionCombo.Delete(descriptionCombo.Count - 1);
			descriptionCombo.Insert(newDesc, 0);
			
			// set added description as active
			gadu.GGDescription = newDesc;
			// description should be set after app start
			mainFrame.AppCfg.UserConf.EmptyDesc = false;
			
			descriptionsMutex.ReleaseMutex();
		}
		
		/// <summary>
		/// Moves description to the first position on the
		/// list, sets it as active descrption and update gui
		/// </summary>
		/// <param name="position">
		/// A <see cref="System.Int32"/>
		/// position of description that should be moved
		/// </param>
		public void MoveUpDescription(int position)
		{
			descriptionsMutex.WaitOne();
			
			if (descriptions.Length - 1 < position)
			{
#if LOG
				logger.Error(String.Format(Catalog.GetString("Requested position: {0}, but description list has only: {1} items"), position, descriptions.Length));
#endif
				throw new ArgumentOutOfRangeException(String.Format(Catalog.GetString("Requested position: {0}, but description list has only: {1} items"), position, descriptions.Length));
			}
			
			if (position < 0)
			{
#if LOG
				logger.Error(String.Format(Catalog.GetString("Requested position: {0}, doesn't exist in description list"), position));
#endif
				throw new ArgumentOutOfRangeException(String.Format(Catalog.GetString("Requested position: {0}, doesn't exist in description list"), position));
			}
			
			// update description list
			string tmp = descriptions[position];
			for (int i = position; i > 0; i--)
				descriptions[i] = descriptions[i-1];
			descriptions[0] = tmp;
			
			// update description combo control
			descriptionCombo.Delete(position);
			descriptionCombo.Insert(descriptions[0], 0);
			
			// set added description as active
			gadu.GGDescription = descriptions[0];
			// description should be set after app start
			mainFrame.AppCfg.UserConf.EmptyDesc = false;
			
			descriptionsMutex.ReleaseMutex();
		}
		
		/// <summary>
		/// Fills description <c>wx.ComboBox</c> with 
		/// descriptions from the internal list
		/// </summary>
		public void FillDescriptionCombo()
		{
#if LOG
			logger.Trace(Catalog.GetString("Filling description combo box"));
#endif
			descriptionsMutex.WaitOne();
			
			descriptionCombo.Clear();
			for (int i = 0; i < SIZE; i++) 
			{
				if (descriptions[i] == null)
					break;
				descriptionCombo.Append(descriptions[i]);
			}
			
			if (descriptions[0] != null && mainFrame.AppCfg.UserConf.EmptyDesc == false)
			{
				descriptionCombo.Select(0);	
				// set added description as active
				gadu.GGDescription = descriptions[0];
			}
			
			if (mainFrame.AppCfg.UserConf.EmptyDesc == true)
				descriptionCombo.Value = string.Empty;
			
			descriptionsMutex.ReleaseMutex();
		}
	}
}
